#include "difference.h"

extern char **code, buf[BYTES];
extern NODE *q;

void convertStringtoBytes(unsigned char *string,int sizeString,unsigned char *bytes){
    int i = 0, k = 0, sum = 8;
    int minor = sizeString;

    for(i=0;i<minor;i++,sum+=8){
        for(;k<sum;k++){
            if( string[k] == '1'){
                bytes[i] |= 0x01;
            }
            if( k < (sum-1) )
                bytes[i] <<= 1;
        }
#ifdef DEBUG
		printf("%d, %x, %d\n",i,bytes[i],bytes[i]);
#endif
    }
}

void convertBytestoString(unsigned char *string,int size_bytes,unsigned char *bytes){
	int i = 0, k = 0, sum = 8, des;
	unsigned char aux, tmp;
	
	for(i=0;i<size_bytes;i++,sum+=8){ 
		aux = bytes[i];
		des = 7;
		for( ; k<sum ; k++,aux<<=1){
			tmp = aux >> des;		
            if( (tmp & 0x01) == 1 ){
                string[k] = '1';
            }
			else{
                string[k] = '0';
			}
        }
    }
#ifdef DEBUG
	printf("tamanho %d %d\n",i,k);
#endif
}

char convertBytesToInt(unsigned char *string,int init,int end){
	int i, two = 1;
	int result = 0;
	int flag = 0;

	if(string[init] == '0') {
		flag = 1;
		for(i=end;i>=init;i--)
			string[i] = (string[i] == '0')? '1':'0';
	}

	for(i=end;i>=init;i--,two *= 2){
		if(string[i] == '1')
			result += two*1;
		else
			result += two*0;	
	}

	if( flag == 1)
		result *= -1;

	return (char) result;
}

void itoa(unsigned char num,char *bin){
	
	unsigned char aux = num, resul[CODE_MAX];
	unsigned char index = 0;
	
	while( aux ){
		resul[index] = aux%2;
		index++;
		aux /= 2;
	}
 
 	int i = index-1, j = 0;
 	for(;i>=0;i--,j++){
 		if(resul[i] == 1)
 			bin[j] = '1';
 		else	
 			bin[j] = '0';
 	}
 	
 	bin[index] = '\0';
}

void auxCreateSSS(char *diff,int size,short *sss,char **value,int index){
    char *bin = (char*)malloc(sizeof(char)*CODE_MAX);
    int k,i,aux;

    for (i=0;i<size;i++,index++){

#ifdef DEBUG
		printf("SSS: %d %d",i,diff[i]);
#endif
		aux = diff[i];
        if(aux<0){

            aux = -aux;
            itoa(aux,bin);

            for(k=0; k<strlen(bin); k++){
                if(bin[k] == '0'){
                    bin[k] = '1';
                }
                else if(bin[k] == '1'){
                    bin[k] = '0';
                }
            }

            sss[index] = strlen(bin);
            strcpy(value[index],bin);
        }
        else if(aux==0){
            sss[index]=0;
            value[index]="";
        }
        else{
            itoa(aux,bin);
            sss[index] = strlen(bin);
            strcpy(value[index],bin);
        }

#ifdef DEBUG
		printf(" %d %s\n",sss[index],value[index]);
#endif

    }

    free(bin);
}

void algorithmCompressor(BMP bmp,FILE *output){

    int tam = bmp.bmpHeader.BiSizeImag;

    short *sss = NULL;
    char *aux,*diff;

    aux = ( char *)malloc(sizeof( char) * tam);
    diff = ( char *)malloc(sizeof(char)* tam );
    sss = (short *)malloc(sizeof(short)* tam);

    char **value, **mem_codes;
    mem_codes = (char**)malloc(sizeof(char*)*tam);
    value = (char**)malloc(sizeof(char*)*tam);

    int i;
    for(i=0; i<tam; i++){
        aux[i] = bmp.image[i];
        mem_codes[i] = (char*)malloc(CODE_MAX*2);
        value[i] = (char*)malloc(CODE_MAX);
    }

#ifdef DEBUG
    printf("difference's Array \n");
#endif 

    diff[0] = aux[0];
    for( i=0; i< tam ; i++){
        diff[i+1] = aux[i+1] - aux[i];
    }

    auxCreateSSS(diff,tam,sss,value,0);

    
#ifdef DEBUG

    int max = initShortHuffmanTree(sss,tam);
    printf("%d\n",max);
    for (i = 0; i <max; i++)
        if(code[i][0] != '\0')
            printf("%d: %s\n", i, code[i]);
#else

	initShortHuffmanTree(sss,tam);

#endif
    
    for(i=0;i<tam;i++){
        strcpy(mem_codes[i], code[sss[i]]);
        strcat(mem_codes[i], value[i]);

#ifdef DEBUG
		char bananana[100];
		strcpy(bananana,value[i]);
		int jacaAA = convertBytesToInt((unsigned char*)bananana,0,strlen(value[i])-1);
       	printf("DIFF: %d %d %s\n",jacaAA,diff[i], value[i]);
#endif

    }
    
    int tam_bytes = tam;
    while( (tam_bytes%8) )
        tam_bytes++;

	tam_bytes = tam_bytes*2*CODE_MAX;
#ifdef DEBUG
	printf("tam_bytes: %d\n",tam_bytes);    
#endif
	
	char *string = (char *)malloc(sizeof(char)*tam_bytes);
    string[0] = '\0';
    for(i=0;i<tam;i++){
        strcat(string,mem_codes[i]);
    }

#ifdef DEBUG
	printf("tam_bytes: %d %d\n",tam_bytes,(int)strlen(string));    
#endif

	tam_bytes = strlen(string);
	while( (tam_bytes%8) )
        tam_bytes++;

    int size_bytes = tam_bytes/8;
    char *bytes = (char *)malloc(sizeof(char)*(size_bytes+1));
    memset(bytes,0,size_bytes);
    bytes[size_bytes] = '\0';
	
    convertStringtoBytes((unsigned char *)string,size_bytes,(unsigned char *)bytes);
    saveBMPHeaderAndDifference(bmp.fileHeader,bmp.bmpHeader,(unsigned char *)bytes,size_bytes,output);
    
	free(sss);
    free(aux);
    free(diff);
    free(mem_codes);
    free(value);
}
